using System;
using System.Collections;
using System.Collections.Generic;

namespace BehaveSharp
{
    public class Story
    {
        private readonly string _title = null;
        private readonly IMessageProvider _messageProvider = null;
        private readonly Hashtable _actions = null;
        private List<Scenario> _scenarios = null;

        public Story(string title) : this(title, MessageProviderRegistry.GetInstance()) { }

        public Story(string title, IMessageProvider messageProvider)
        {
            _title = title;
            _messageProvider = messageProvider;
            _scenarios = new List<Scenario>();
            _actions = new Hashtable();

            if (!string.IsNullOrEmpty(_title))
            {
                _messageProvider.AddMessage("Story: " + title);
            }
        }

        public AsAFragment AsA(string role)
        {
            return new AsAFragment(role, this);
        }

        public Scenario WithScenario(string title)
        {
            Scenario scenario = new Scenario(title, this);

            _scenarios.Add(scenario);

            AddMessage("");
            AddMessage(string.Format("\tScenario {0}: {1}", _scenarios.Count, scenario.Title));

            return scenario;
        }

        internal void AddMessage(string message)
        {
            _messageProvider.AddMessage(message);
        }

        internal void InvokeAction(string type, string message, Action action)
        {
            action();
            AddMessage(type + " " + message);

            CatalogAction(message, action);
        }

        internal void InvokeAction<TArg0>(string type, string message, Action<TArg0> action, TArg0 arg0)
        {
            action(arg0);
            AddMessage(type + " " + message + ": " + arg0.ToString());

            CatalogAction(message, action);
        }

        internal void InvokeAction<TArg0, TArg1>(string type, string message, Action<TArg0, TArg1> action, TArg0 arg0, TArg1 arg1)
        {
            action(arg0, arg1);
            AddMessage(type + " " + message + ": (" + arg0.ToString() + ", " + arg1.ToString() + ")");

            CatalogAction(message, action);
        }

        internal void InvokeAction<TArg0, TArg1, TArg2>(string type, string message, Action<TArg0, TArg1, TArg2> action, TArg0 arg0, TArg1 arg1, TArg2 arg2)
        {
            action(arg0, arg1, arg2);
            AddMessage(type + " " + message + ": (" + arg0.ToString() + ", " + arg1.ToString() + ", " + arg2.ToString() + ")");

            CatalogAction(message, action);
        }

        internal void InvokeAction<TArg0, TArg1, TArg2, TArg3>(string type, string message, Action<TArg0, TArg1, TArg2, TArg3> action, TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            action(arg0, arg1, arg2, arg3);
            AddMessage(type + " " + message + ": (" + arg0.ToString() + ", " + arg1.ToString() + ", " + arg2.ToString() + ", " + arg3.ToString() + ")");

            CatalogAction(message, action);
        }

        internal void InvokeActionFromCatalog(string type, string message)
        {
            ValidateActionExists(message);

            Action action = (Action)_actions[message];

            InvokeAction(type, message, action);
        }

        internal void InvokeActionFromCatalog<TArg0>(string type, string message, TArg0 arg0)
        {
            ValidateActionExists(message);

            Action<TArg0> action = (Action<TArg0>)_actions[message];

            InvokeAction(type, message, action, arg0);
        }

        internal void InvokeActionFromCatalog<TArg0, TArg1>(string type, string message, TArg0 arg0, TArg1 arg1)
        {
            ValidateActionExists(message);

            Action<TArg0, TArg1> action = (Action<TArg0, TArg1>)_actions[message];

            InvokeAction(type, message, action, arg0, arg1);
        }

        internal void InvokeActionFromCatalog<TArg0, TArg1, TArg2>(string type, string message, TArg0 arg0, TArg1 arg1, TArg2 arg2)
        {
            ValidateActionExists(message);

            Action<TArg0, TArg1, TArg2> action = (Action<TArg0, TArg1, TArg2>)_actions[message];

            InvokeAction(type, message, action, arg0, arg1, arg2);
        }

        internal void InvokeActionFromCatalog<TArg0, TArg1, TArg2, TArg3>(string type, string message, TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            ValidateActionExists(message);

            Action<TArg0, TArg1, TArg2, TArg3> action = (Action<TArg0, TArg1, TArg2, TArg3>)_actions[message];

            InvokeAction(type, message, action, arg0, arg1, arg2, arg3);
        }

        private void CatalogAction(string message, object action)
        {
            if (_actions.ContainsKey(message))
                return;

            _actions.Add(message, action);
        }

        private void ValidateActionExists(string message)
        {
            if (!_actions.ContainsKey(message))
                throw new ActionMissingException(string.Format("Action missing for action '{0}'.", message));
        }

    }

}
